#include "mqtt.h"

void log_error_if_nonzero(const char *message, int error_code)
{
    if (error_code != 0)
    {
        ESP_LOGE(MQTT_TAG, "Last error %s: 0x%x", message, error_code);
    }
}

/*
 * @brief Event handler registered to receive MQTT events
 *
 *  This function is called by the MQTT client event loop.
 *
 * @param handler_args user data registered to the event.
 * @param base Event base for the handler(always MQTT Base in this example).
 * @param event_id The id for the received event.
 * @param event_data The data for the event, esp_mqtt_event_handle_t.
 */
void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(MQTT_TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_CONNECTED");
        /**增加上线通知及上线订阅 */
        msg_id = esp_mqtt_client_publish(client, "/esp32/server", "data_3", 0, 1, 0);

        msg_id = esp_mqtt_client_subscribe(client, "/esp32/client", 0);
        ESP_LOGI(MQTT_TAG, "sent subscribe successful, msg_id=%d", msg_id);

        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DISCONNECTED");
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);

        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        // char* testMemory=(char*)malloc(100*sizeof(int64_t));
        // free(testMemory);
        MQTT_MESSAGE message = {0};
        mqtt_data_parse(event->data, event->data_len, &message);
        xQueueSend(serialMessageQueue, &message, portMAX_DELAY);

        // ESP_LOGI("STRUCT ADDRESS", "%p", (void *)&message);
        print_all_tasks_info();
        print_heap_info("heapInfo");
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(MQTT_TAG, "MQTT_EVENT_ERROR");
        if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT)
        {
            log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
            log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
            log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
            ESP_LOGI(MQTT_TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
        }
        break;
    default:
        ESP_LOGI(MQTT_TAG, "Other event id:%d", event->event_id);
        break;
    }
}
char *create_mqtt_send_data(int command, char *send)
{
    cJSON *root = cJSON_CreateObject();

    cJSON_AddNumberToObject(root, "command", command);
    cJSON_AddStringToObject(root, "data", send);
    char *jsonStr = cJSON_Print(root);
    cJSON_Delete(root);
    return jsonStr;
}
void scheduleSendMqttMsg()
{
    const char *test = "hello";
    while (true)
    {
        char *data = create_mqtt_send_data(20, test);
        esp_mqtt_client_publish(client, MQTT_SEND_TOPIC, data, strlen(data), 1, 0);
        free(data);
        ESP_LOGI("MQTT_SCHEDULE", "SCHEDULE SEND");
        print_all_tasks_info();
        print_heap_info("heapInfo");
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void mqtt_data_parse(char *data, size_t size, MQTT_MESSAGE *message)
{

    cJSON *root = cJSON_Parse(data);
    if (root == NULL)
    {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL)
        {
            ESP_LOGE(MQTT_TAG, "Error before: %s", error_ptr);
        }
        return;
    }

    cJSON *command = cJSON_GetObjectItemCaseSensitive(root, "Command");
    cJSON *rcv = cJSON_GetObjectItemCaseSensitive(root, "url");

    strcpy(message->data, rcv->valuestring);
    memcpy(&message->command, &command->valueint, sizeof(int16_t));
    cJSON_Delete(root);
    root = NULL;
    ESP_LOGI("JSON PARSE", "command:%d url:%s", message->command, message->data);
}

void mqtt_app_start(void)
{
    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = MQTT_ADDRESS,
        .credentials.client_id = MQTT_CLIENT_ID,
        .credentials.username = "admin",
        .credentials.authentication.password = "public",

    };

    client = esp_mqtt_client_init(&mqtt_cfg);

    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);

    // xTaskCreate(scheduleSendMqttMsg, "scheduleSendMqttMsg", 1024 * 3, (void *)3, tskIDLE_PRIORITY + 2, NULL);
}
// 打印堆内存信息

esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    static FILE *file = NULL;
    static int content_length = 0;
    static int downloaded_length = 0;

    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s",
                 evt->header_key, evt->header_value);
        // 获取Content-Length头信息，用于进度显示
        if (strcasecmp(evt->header_key, "Content-Length") == 0)
        {
            content_length = atoi(evt->header_value);
            ESP_LOGI(TAG, "Content-Length: %d bytes", content_length);
        }
        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGE(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);

        // ESP_LOG_BUFFER_HEXDUMP(TAG, evt->data, evt->data_len, ESP_LOG_INFO);
        if (file == NULL)
        {
            // 第一次收到数据时打开文件
            file = fopen(FILE_PATH, "wb");
            if (file == NULL)
            {
                ESP_LOGE(TAG, "Failed to open file for writing");
                return ESP_FAIL;
            }
            ESP_LOGI(TAG, "File opened for writing: %s", FILE_PATH);
        }

        // 将数据写入文件
        if (file && evt->data_len > 0)
        {
            size_t written = fwrite(evt->data, 1, evt->data_len, file);
            if (written != evt->data_len)
            {
                ESP_LOGE(TAG, "Failed to write data to file");
                fclose(file);
                file = NULL;
                return ESP_FAIL;
            }
            downloaded_length += evt->data_len;

            // 打印下载进度
            if (content_length > 0)
            {
                float progress = (float)downloaded_length / content_length * 100;
                ESP_LOGI(TAG, "Download progress: %.1f%% (%d/%d bytes)",
                         progress, downloaded_length, content_length);
            }
            else
            {
                ESP_LOGI(TAG, "Downloaded: %d bytes", downloaded_length);
            }
        }
        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
        if (file)
        {
           
            
        }
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        if (file)
        {
        
            ESP_LOGW(TAG, "Connection disconnected before download completed");
              fclose(file);
            file = NULL;
            ESP_LOGI(TAG, "File download completed");
            size_t total = 0, used = 0;
            esp_spiffs_info(NULL, &total, &used);
            ESP_LOGE(TAG, "SPIFFS partition size: total: %d bytes, used: %d bytes  rate:%f", total, used, (float)((float)used / total));
            // list_spiffs_files();
            // dump_file_hex("411APP.bin");

            FILE *file = fopen(FILE_PATH, "rb"); // 以二进制模式读取
            if (file == NULL)
            {
                ESP_LOGE(TAG, "Failed to open file: ");
                return ESP_FAIL;
            }

            // 获取文件大小
            fseek(file, 0, SEEK_END);
            size_t file_size = ftell(file);
            fseek(file, 0, SEEK_SET);
            ESP_LOGI("DOWNLOAD_FILE", "DOWNLOAD_FILESIZE:%d", file_size);

            ymodem_config_t ymodem_cfg = {
                .uart_port = UART_NUM_1,
                .tx_timeout_ms = 5000,
                .max_retries = 10,
                .block_size = 1024 // 使用1K块大小
            };

            // 发送文件
            const char *filename = "/spiffs/downloaded_file.bin";
            ymodem_status_t status = ymodem_send_file(filename, &ymodem_cfg,file,file_size);
        }
        break;
    case HTTP_EVENT_REDIRECT:

        break;
    default:
        ESP_LOGW(TAG, "Unhandled HTTP event: %d", evt->event_id);
        break;
    }
    return ESP_OK;
}

void queueConsumerTask(void *pvParameters)
{

    MQTT_MESSAGE message;
    for (;;)
    {
        if (xQueueReceive(serialMessageQueue, &message, portMAX_DELAY) == pdTRUE)
        {
            ESP_LOGI(xQueue_Consumer_TAG, "COMMAND:%d  url:%s", message.command, message.data);

            esp_http_client_config_t config = {
                .url = DOWNLOAD_URL,
                .event_handler = http_event_handler,
                .timeout_ms = 30000, // 30秒超时
                .buffer_size = FILE_WRITE_BUFFER_SIZE,
                .disable_auto_redirect = false,
            };

            // 创建HTTP客户端
            esp_http_client_handle_t client = esp_http_client_init(&config);

            ESP_LOGI(TAG, "Starting file download from: %s", DOWNLOAD_URL);
            esp_err_t err = esp_http_client_perform(client);

            if (err == ESP_OK)
            {
                int status_code = esp_http_client_get_status_code(client);
                if (status_code == 200)
                {
                    ESP_LOGI(TAG, "Download successful, HTTP status code: %d", status_code);
                }
                else
                {
                    ESP_LOGE(TAG, "HTTP request returned non-OK status: %d", status_code);
                    err = ESP_FAIL;
                }
            }
            else
            {
                ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
            }

            // 清理资源
            esp_http_client_cleanup(client);
        }
    }
}
